package ifs.RnD.svnRead;

import ifs.RnD.Graph.RepositoryGraph;
import ifs.RnD.Graph.Favourite;
import ifs.RnD.revisionRead.DisplayReposiroryHistory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import org.tmatesoft.svn.core.ISVNDirEntryHandler;
import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNPropertyData;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

/*
 * This class is to get the repository tree at the latest (HEAD)
 * revision starting with the directory that is the path/to/repository part of
 * the repository location URL. The main point is SVNRepository.getDir() method
 * that is called recursively for each directory (till the end of the tree).
 * getDir collects all entries located inside a directory and returns them as a
 * java.util.Collection. As an example here's one of the program layouts (for
 * the default url used in the program ):
 */
public class DisplayRepositoryTree {

     /*
      * args parameter is used to obtain a repository location URL, user's
      * account name & password to authenticate him to the server.
      */
     ArrayList<String> pathList;
     public Tree directTree;
     String url;
     String repositoryRoot;
     /**
      * SVNRepository object which contains the records about the repository
      */
     public SVNRepository repository;
     int rootLenth;
     SVNClientManager SvnClientMgr;
     ISVNAuthenticationManager authManager;
     private String uName;
     private String pWord;
     /**
      * Used to set the authentication state
      */
     public boolean authStatus;
     Main mainInstance;
     Favourite fav;
     RepositoryGraph canverse;

     /**
      * initializes the library (it must be done before ever using the
      * library itself) this will initialte the repository objects
      * @param url
      * @param user
      * @param pass
      * @param mainInstance
      * @throws SVNException
      */
     public DisplayRepositoryTree(String url, String user, String pass, Main m, Favourite fav) throws SVNException {

          /*
           * initializes the library (it must be done before ever using the
           * library itself) this will initialte the repository objects
           */
          this.mainInstance = m;
          this.fav = fav;
          this.uName = user;
          this.pWord = pass;
          setupLibrary();
          authManager = SVNWCUtil.createDefaultAuthenticationManager(uName, pWord);

          cerateRepoToURL(url);
          if (authStatus) {
               try {
                    repositoryRoot = repository.getRepositoryRoot(true).toString();
                    repository = null;
                    System.out.println("Repo root - " + repositoryRoot);
                    cerateRepoToURL(repositoryRoot);

               } catch (SVNException ex) {
                    System.out.println("throw");
               }

               String[] words = repositoryRoot.trim().split("/");
               rootLenth = (words.length);
               Node node = new Node(words[words.length - 1], "ROOT", repositoryRoot, null);
               directTree = new Tree(node, repositoryRoot);
               pathList = new ArrayList<String>();
               readRepository(repository, "");
               m.authst = true;
          } else {
               m.authst = false;
               m.reset();
          }
     }

     /**
      * This will genarate a new repository object which will be used when a given URL will recursivly reading each level
      * of the given URL
      * @param url
      * @param canverse
      */
     public void ReadNewRepository(String url, RepositoryGraph canverse) throws SVNException {
          try {
               repositoryRoot = repository.getRepositoryRoot(true).toString();
               repository = null;
               System.out.println("Repo root - " + repositoryRoot);
               cerateRepoToURL(repositoryRoot);

          } catch (SVNException ex) {
               ex.printStackTrace();
               System.out.println("throw");
          }

          String[] words = repositoryRoot.trim().split("/");
          rootLenth = (words.length);
          Node node = new Node(words[words.length - 1], "ROOT", repositoryRoot, null);
          directTree = new Tree(node, repositoryRoot);
          pathList = new ArrayList<String>();
          processLink(url, this, canverse);

     }

     /**
      * This will return the instance of the Main class. this will be used in repository auhentication
      * @return instance of main class
      */
     public Main getMain() {
          return mainInstance;
     }

     /**
      * This will return the user name of the user
      * @return user name
      */
     public String getUserName() {
          return uName;
     }

     /**
      * This will return the password of the user
      * @return password
      */
     public String getPassword() {
          return pWord;
     }

     /**
      * This will return the instance of the repository
      * @return repository instance
      */
     public SVNRepository getRepository() {
          return repository;
     }

     /*
      * Initializes the library to work with a repository via
      * different protocols.
      */
     
     private static void setupLibrary() {
          /*
           * For using over http:// and https://
           */
          DAVRepositoryFactory.setup();
          /*
           * For using over svn:// and svn+xxx://
           */
          SVNRepositoryFactoryImpl.setup();
          /*
           * For using over file:
           */
          FSRepositoryFactory.setup();
     }

     /**
      * create a repository to the given url
      * @param url
      */
     public void cerateRepoToURL(String url) {
          setURL(url);
          setupLibrary();
          System.out.println("Repo Reading  = " + url);
          repository = null;
          try {
               boolean notRepositoryRoot = true;
               SVNURL realRoot = null;
               /*
                * Creates an instance of SVNRepository to work with the repository.
                * All user's requests to the repository are relative to the
                * repository location used to create this SVNRepository.
                * SVNURL is a wrapper for URL strings that refer to repository locations.
                */
               //while (notRepositoryRoot){
               repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(url));
               repository.getAuthenticationManager();
               repository.setAuthenticationManager(authManager);
               testRepositoryRoot(url, authManager);
               authStatus = true;
               //repository.setAuthenticationManager(authManager);


          } catch (SVNException ex) {
               /*
                * Perhaps a malformed URL is the cause of this exception
                */
               JOptionPane.showMessageDialog(null, "INVALID CREDENTIALS!", "Invalid Login !", 0);
               authStatus = false;
          }

     }

     /**
      * This will test the authentication of the repository root
      * @param urlStr
      * @param authManager
      * @throws SVNException
      */
     public void testRepositoryRoot(String urlStr, ISVNAuthenticationManager authManager) throws SVNException {
          SVNURL url = SVNURL.parseURIEncoded(urlStr);
          SVNClientManager SVNinstance = SVNClientManager.newInstance();
          SVNinstance.setAuthenticationManager(authManager);
          SVNinstance.getLogClient().doList(url, SVNRevision.HEAD, SVNRevision.HEAD, false, false, new ISVNDirEntryHandler() {

               @Override
               public void handleDirEntry(SVNDirEntry entry) throws SVNException {
                    return;
               }
          });
     }

     /**
      * This will recursively read the different levels of the repository on given url for its components
      * @param url
      * @param disRepoTree
      * @throws SVNException
      */
     public void processLink(String url, DisplayRepositoryTree disRepoTree) throws SVNException {
          String urlWords[];
          urlWords = url.split("/");
          String rootWords[];
          rootWords = repositoryRoot.split("/");
          String pathComp[] = new String[urlWords.length - rootWords.length];
          String tempPath = repositoryRoot;
          Node root = null;

          for (int i = 0; i < pathComp.length; i++) {
               pathComp[i] = urlWords[rootWords.length + i];
               tempPath = tempPath + "/" + urlWords[rootWords.length + i];
               System.out.println("Process link: " + tempPath);
               disRepoTree.cerateRepoToURL(tempPath);
               root = disRepoTree.readRepository(disRepoTree.getRepository(), "");
          }

          canverse = new RepositoryGraph(disRepoTree, fav);

          List list = (List) disRepoTree.directTree.toList();
          Iterator iterator2 = list.iterator();
          while (iterator2.hasNext()) {
               Node node = (Node) iterator2.next();
               String name = node.getData();
               if (name.equals(urlWords[urlWords.length - 1])) {
                    canverse.drawGraph(node);
                    canverse.setVisible(true);
                    break;
               }
          }
     }

     /**
      * This will recursively read the different levels of the repository on given url for its components
      * @param url
      * @param disRepoTree
      * @throws SVNException
      */
     public void processLink(String url, DisplayRepositoryTree disRepoTree, RepositoryGraph canverse) throws SVNException {
          String urlWords[];
          urlWords = url.split("/");
          String rootWords[];
          rootWords = repositoryRoot.split("/");
          String pathComp[] = new String[urlWords.length - rootWords.length];
          String tempPath = repositoryRoot;
          Node root = null;

          for (int i = 0; i < pathComp.length; i++) {
               pathComp[i] = urlWords[rootWords.length + i];
               tempPath = tempPath + "/" + urlWords[rootWords.length + i];
               System.out.println("Process link: " + tempPath);
               disRepoTree.cerateRepoToURL(tempPath);
               root = disRepoTree.readRepository(disRepoTree.getRepository(), "");
          }


          List list = (List) disRepoTree.directTree.toList();
          Iterator iterator2 = list.iterator();
          while (iterator2.hasNext()) {
               Node node = (Node) iterator2.next();
               String name = node.getData();
               if (name.equals(urlWords[urlWords.length - 1])) {
                    canverse.drawGraph(node);
                    canverse.setVisible(true);
                    break;
               }

          }

     }

     /**
      * @param repository
      * @param path
      * @throws SVNException
      * @return
      */
     public Node readRepository(SVNRepository repository, String path) throws SVNException {
          Node tempNode = null;
          try {
               /*
                * Checks up if the specified path/to/repository part of the URL
                * really corresponds to a directory. If doesn't the program exits.
                * SVNNodeKind is that one who says what is located at a path in a
                * revision. -1 means the latest revision.
                */
               SVNNodeKind nodeKind = repository.checkPath("", -1);
               if (nodeKind == SVNNodeKind.NONE) {
                    JOptionPane.showMessageDialog(null, "There is no entry at '" + url + "'.");
                    System.exit(1);
               } else if (nodeKind == SVNNodeKind.FILE) {
                    JOptionPane.showMessageDialog(null, "This is a File");
                    System.exit(1);
               }
               /*
                * getRepositoryRoot() returns the actual root directory where the
                * repository was created. 'true' forces to connect to the repository
                * if the root url is not cached yet.
                */
               System.out.println("");
               /*
                * Displays the repository tree at the current path - "" (what means
                * the path/to/repository directory)
                */
               tempNode = this.listEntries(repository, path);
          } catch (SVNException svne) {
               System.err.println("error while listing entries: " + svne.getMessage());
               System.exit(1);
          }
          /*
           * Gets the latest revision number of the repository
           */
          long latestRevision = -1;
          try {
               latestRevision = repository.getLatestRevision();
          } catch (SVNException svne) {
               System.err.println("error while fetching the latest repository revision: "
                       + svne.getMessage());
               System.exit(1);
          }
          System.out.println("");
          System.out.println("---------------------------------------------");
          return tempNode;

     }

     /*
      * Called recursively to obtain all entries that make up the repository tree
      * repository - an SVNRepository which interface is used to carry out the
      * request, in this case it's a request to get all entries in the directory
      * located at the path parameter;
      *
      * path is a directory path relative to the repository location path (that
      * is a part of the URL used to create an SVNRepository instance);
      *
      */
     /**
      * @param repository
      * @param path
      * @return
      * @throws SVNException
      */
     public Node listEntries(SVNRepository repository, String path)
             throws SVNException {
          /*
           * Gets the contents of the directory specified by path at the latest
           * revision (for this purpose -1 is used here as the revision number to
           * mean HEAD-revision) getDir returns a Collection of SVNDirEntry
           * elements. SVNDirEntry represents information about the directory
           * entry. Here this information is used to get the entry name, the name
           * of the person who last changed this entry, the number of the revision
           * when it was last changed and the entry type to determine whether it's
           * a directory or a file. If it's a directory listEntries steps into a
           * next recursion to display the contents of this directory. The third
           * parameter of getDir is null and means that a user is not interested
           * in directory properties. The fourth one is null, too - the user
           * doesn't provide its own Collection instance and uses the one returned
           * by getDir.
           */

          Collection entries = repository.getDir(path, -1, null, (Collection) null);
          Iterator iterator = entries.iterator();
          this.pathList.clear();
          while (iterator.hasNext()) {
               SVNDirEntry entry = (SVNDirEntry) iterator.next();
               String dirPath = (url + "/" + (path.equals("") ? "" : path + "/") + entry.getName());
               String type;

               if (entry.getKind() == SVNNodeKind.DIR) {
                    type = "DIR";
               } else {
                    type = "FILE";
               }

               String words[];
               words = dirPath.split("/");
               String pathComp[] = new String[words.length - rootLenth];

               for (int i = 0; i < pathComp.length; i++) {
                    pathComp[i] = words[rootLenth + i];
                    //System.out.println(words[rootLenth + i]);
               }
               directTree.insertNode(pathComp, type, dirPath, 0, directTree.getRootElement());
          }

          return directTree.getRootElement();
     }

     /**
      * @param url
      */
     public void setURL(String url) {
          this.url = url;
     }

     /**
      * @return
      */
     public String getURL() {
          return url;
     }

     /**
      * @return
      */
     public String getRootURL() {
          return repositoryRoot;
     }

     /**
      * reads content type 'externals'
      * @param exUrl
      * @return
      */
     public boolean readExternals(String exUrl) {
          try {
               SVNURL ExSVNurl = SVNURL.parseURIEncoded(exUrl);
               if (SvnClientMgr == null) {
                    SvnClientMgr = SVNClientManager.newInstance();
               }
               SVNPropertyData externalData = SvnClientMgr.getWCClient().doGetProperty(ExSVNurl, "svn:externals", SVNRevision.HEAD, SVNRevision.HEAD);
               if (externalData != null) {
                    String dataStream = externalData.getValue().toString();

                    String[] urlWords = exUrl.split("/");
                    String pathComp[] = new String[urlWords.length - rootLenth + 1];

                    for (int i = 0; i < pathComp.length - 1; i++) {
                         pathComp[i] = urlWords[rootLenth + i];
                    }

                    String words[];
                    int index = 0;
                    words = dataStream.split("[\\s]+");

                    for (int i = 0; i < (words.length / 2); i++) {
                         String type = "EXTERNAL";
                         index = 2 * i;
                         pathComp[pathComp.length - 1] = words[index + 1];
                         directTree.insertExtNode(pathComp, type, words[index], 0, directTree.getRootElement());
                    }
                    return true;
               } else {
                    return false;
               }

          } catch (Exception ex) {
               Logger.getLogger(DisplayReposiroryHistory.class.getName()).log(Level.SEVERE, null, ex);
               return false;
          }
     }
}
